﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace ArrowScheduleCore
{
    /// <summary>
    /// Properties specific to a monthly and yearly schedule.
    /// </summary>
    public abstract class ScheduleMonth : SchedulePeriod
    {

        /// <summary>
        /// How to schedule the day in the month to run.
        /// </summary>
        public enum CYCLE
        {
            /// <summary>
            /// Schedule on a specific day, such as the 26th.
            /// </summary>
            DAY,
            /// <summary>
            /// Schedule based on the week and day of the week.
            /// </summary>
            WEEK
        };

        /// <summary>
        /// For a Cycle of WEEK, this is the week to run in. This enum has the values 0...4 and that will not change so
        /// you can also use week offset from the start of the month as an integer.
        /// </summary>
        public enum WEEK
        {
            /// <summary>Run the first week of the month.</summary>
            FIRST = 0,
            /// <summary>Run the second week of the month.</summary>
            SECOND = 1,
            /// <summary>Run the third week of the month.</summary>
            THIRD = 2,
            /// <summary>Run the fourth week of the month.</summary>
            FOURTH = 3,
            /// <summary>Run the last (4th or 5th) week of the month.</summary>
            LAST = 4
        };

        /// <summary>
        /// For a Cycle of WEEKDAY this is the day in WEEK to run. The int values for this will always be SUNDAY + 6 == SATURDAY but
        /// the value of SUNDAY is not locked down.
        /// </summary>
        public enum WEEKDAY
        {
            /// <summary>Run the first day of the month.</summary>
            FIRST_DAY = 0,
            /// <summary>Run the first weekday of the month.</summary>
            FIRST_WEEKDAY = 1,
            /// <summary>Run the first weekend of the month.</summary>
            FIRST_WEEKEND = 2,
            /// <summary>Run the first Sunday of the month.</summary>
            SUNDAY = 3,
            /// <summary>Run the first Monday of the month.</summary>
            MONDAY = 4,
            /// <summary>Run the first Tuesday of the month.</summary>
            TUESDAY = 5,
            /// <summary>Run the first Wednesday of the month.</summary>
            WEDNESDAY = 6,
            /// <summary>Run the first Thursday of the month.</summary>
            THURSDAY = 7,
            /// <summary>Run the first Friday of the month.</summary>
            FRIDAY = 8,
            /// <summary>Run the first Saturday of the month.</summary>
            SATURDAY = 9
        };

        /// <summary>
        /// Set if the CYCLE type. runCycle in the database.
        /// </summary>
        protected CYCLE cycle;
        /// <summary>
        /// Set the day to run on if set to run a specific day of each month. runDay in the database.
        /// </summary>
        protected int day;
        /// <summary>
        /// Set the week to run on if set to run a specific week of each month. runWeek in the database.
        /// </summary>
        protected WEEK week;
        /// <summary>
        /// Set the day of the week to run on if set to run a specific week of each month. runWeekday in the database.
        /// </summary>
        protected WEEKDAY weekday;

        /// <summary>
        /// Give a human readable version of the WEEK setting.
        /// </summary>
        protected string WeekAsString
        {
            get
            {
                switch (week)
                {
                    case WEEK.FIRST:
                        return GlobalStrings.ScheduleMonthly_calWeekFirst;
                    case WEEK.SECOND:
                        return GlobalStrings.ScheduleMonthly_calWeekSecond;
                    case WEEK.THIRD:
                        return GlobalStrings.ScheduleMonthly_calWeekThird;
                    case WEEK.FOURTH:
                        return GlobalStrings.ScheduleMonthly_calWeekFourth;
                    case WEEK.LAST:
                        return GlobalStrings.ScheduleMonthly_calWeekLast;
                }
                throw new ApplicationException("unknown WEEK " + week);
            }
        }

        /// <summary>
        /// Give a human readable version of the WEEKDAY setting.
        /// </summary>
        protected string WeekdayAsString
        {
            get
            {
                switch (weekday)
                {
                    case WEEKDAY.FIRST_DAY:
                        return GlobalStrings.ScheduleMonthly_calWeekFirstDay;
                    case WEEKDAY.FIRST_WEEKDAY:
                        return GlobalStrings.ScheduleMonthly_calWeekFirstWeekday;
                    case WEEKDAY.FIRST_WEEKEND:
                        return GlobalStrings.ScheduleMonthly_calWeekFirstWeekend;
                }
                DateTimeFormatInfo info = CultureInfo.CurrentUICulture.DateTimeFormat;
                return info.GetDayName((DayOfWeek)(weekday - 3));
            }
        }

        /// <summary>
        /// Schedule is based on day of the month or week and day of the week.
        /// </summary>
        public CYCLE Cycle
        {
            get { return cycle; }
            set { cycle = value; }
        }

        /// <summary>
        /// If Cycle is DAY, this is the day of the month to run.
        /// </summary>
        public int Day
        {
            get { return day; }
            set { day = value; }
        }

        /// <summary>
        /// If Cycle is WEEK, this is which week in the month to run.
        /// </summary>
        public WEEK Week
        {
            get { return week; }
            set { week = value; }
        }

        /// <summary>
        /// If Cycle is WEEK, this is which day in the week to run.
        /// </summary>
        public WEEKDAY Weekday
        {
            get { return weekday; }
            set { weekday = value; }
        }
    }

    /// <summary>
    /// Properties specific to a monthly schedule. This is set to either "run on day X of every Nth month(s)" or
    /// "the WEEK WEEKDAY of every Nth month(s)."
    /// </summary>
    public class ScheduleMonthly : ScheduleMonth
    {

        private int period;	// DB: runPeriod

        /// <summary>
        /// Create an object.
        /// </summary>
        /// <param name="cycle">The cycle to run on.</param>
        /// <param name="day">The day of the month to run.</param>
        /// <param name="week">The week in the month to run.</param>
        /// <param name="weekday">The day in the week to run.</param>
        /// <param name="period">Run every N (period) month.</param>
        public ScheduleMonthly(CYCLE cycle, int day, WEEK week, WEEKDAY weekday, int period)
        {
            this.cycle = cycle;
            this.day = day;
            this.week = week;
            this.weekday = weekday;
            this.period = period;
        }

        /// <summary>
        /// Create an object.
        /// </summary>
        /// <param name="day">The day of the month to run.</param>
        /// <param name="period">Run every N (period) month.</param>
        public ScheduleMonthly(int day, int period)
        {
            cycle = CYCLE.DAY;
            this.day = day;
            this.period = period;
        }

        /// <summary>
        /// Create an object.
        /// </summary>
        /// <param name="week">The week in the month to run.</param>
        /// <param name="weekday">The day in the week to run.</param>
        /// <param name="period">Run every N (period) month.</param>
        public ScheduleMonthly(WEEK week, WEEKDAY weekday, int period)
        {
            cycle = CYCLE.WEEK;
            this.week = week;
            this.weekday = weekday;
            this.period = period;
        }

        /// <summary>
        /// Run every N (period) month.
        /// </summary>
        public int Period
        {
            get { return period; }
            set { period = value; }
        }

        /// <summary>
        /// Use this instead of class type only when necessary such as a switch statement.
        /// </summary>
        public override REPEAT RepeatType
        {
            get { return REPEAT.MONTHLY; }
        }

        /// <summary>
        /// A human readable description of the period.
        /// </summary>
        public override string Description
        {
            get
            {
                if (cycle == CYCLE.DAY)
                    return String.Format(GlobalStrings.ScheduleMonthly_schdMonDay, period, day);
                return String.Format(GlobalStrings.ScheduleMonthly_schdMonWeekDay, period, WeekAsString, WeekdayAsString);
            }
        }
    }
}
